/**
  SUNNY project, Anyang Normal University, IMP-CAS
  \class TASparseMatrix
  \brief dedicated for Storage of sparse matrix. The class is a vector of matrix
  columns, with each column stored in a ROOT TTree object - TATreeCol
  \author SUN Yazhou, asia.rabbit@163.com
  \since 2021/02/12
  \date Last modified: 2021/02/12 by SUN Yazhou
  \copyright 2020-2021 SUN Yazhou
  \copyright SUNNY project, Anyang Normal University, IMP-CAS
*/

#include <algorithm>
#include <iostream>
#include "TASparseMatrix.h"
#include "TASparseVec.h"
#include "TAException.h"

using std::for_each;
using std::cout;
using std::endl;

TASparseMatrix::TASparseMatrix(){}

TASparseMatrix::~TASparseMatrix(){}

/// r = (*this)*s
void TASparseMatrix::DotProduct(const vec_t<double> &s, TASparseVec &r) const{
  const ull m = size();
  if(s.size() != m) TAException::Error("TASparseMatrix", "Dimension Mismatch.");

  ull n1 = (*std::min_element(begin(), end(),
    [](TASparseVec *a, TASparseVec *b){ return a->GetRow(0) < b->GetRow(0); })
      )->GetRow(0);
  ull n2 = (*std::max_element(begin(), end(),
    [](TASparseVec *a, TASparseVec *b){ return a->GetMaxRow() < b->GetMaxRow(); })
      )->GetMaxRow();

  double e1, e2;
  for(ull i = n1; i <= n2; i++){
    double t = 0.;
    for(ull j = 0; j < m; j++)
      if((e1 = s[j]) && (e2 = (*this->at(j))[i])) t += e1*e2;
    if(t) r.Fill(i, t);
  } // end for over i
} // end member function DotProduct

// r = (*this)*s
void TASparseMatrix::DotProduct(const matrix &s, TASparseMatrix &r) const{
  r.Clear(); // empty r

  const int n = size();
  for(int i = 0; i < n; i++){
    r.push_back(new TASparseVec("r"));
    DotProduct(s.cv(i), *r[i]);
  } // end for over i
} // end member function DotProduct

void TASparseMatrix::PushBackColumn(const TASparseVec &r){
  push_back(new TASparseVec(r));
} // end member function PushBackColumn

/// Erase [c1, c2)
void TASparseMatrix::EraseColumn(int c1, int c2){
  if(c1 == c2) TAException::Error("TASparseMatrix", "[c1,c2]: 0 length.");
  if(c2 > int(size()) || c1 < 0)
    TAException::Error("TASparseMatrix",
      "EraseColumn: Designated range out of border.");
  for_each(begin()+c1, begin()+c2, [](TASparseVec *c){ delete c; });
  erase(begin()+c1, begin()+c2);
} // end member funtion EraseColumn

void TASparseMatrix::Print() const{
  cout << "Totally " << size() << " columns." << endl;
  for_each(begin(), end(), [](const TASparseVec *c){ c->Print(); });
} // end member function Print

// print in C/C++ form
void TASparseMatrix::PrintInC(ull maxLen) const{
  cout << "Totally " << size() << " vectors." << endl;
  for_each(begin(), end(), [maxLen](const TASparseVec *c){ c->PrintInC(maxLen); });
} // end member function PrintInC

// erase all columns
void TASparseMatrix::Clear(){
  if(!size()) return;
  EraseColumn(0, size());
} // end member function Clear

/// only transfer ownership. r is emptied.
void TASparseMatrix::MoveBack(TASparseMatrix &r){
  for_each(r.begin(), r.end(), [this](TASparseVec *c){ push_back(c); });
  r.clear();
} // end member function MoveBack

void TASparseMatrix::Save(){
  for_each(begin(), end(), [](TASparseVec *c){ c->Save(); });
} // end member function Save
